package com.itaming.lycheeframework.support.cipher.asymmetric;

import com.itaming.lycheeframework.support.cipher.AbstractEncryptor;
import org.springframework.util.Assert;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.KeySpec;

/**
 * 非对称加解密器抽象类
 *
 * @author A.Ming
 */
public abstract class AbstractAsymmetricEncryptor extends AbstractEncryptor<AsymmetricEncryptorOptions> {

    /**
     * 加密
     *
     * @param data    待加密数据
     * @param options 加密器选项
     * @return 加密后的数据
     */
    @Override
    public byte[] encrypt(byte[] data, AsymmetricEncryptorOptions options) {
        checkOptions(options);
        Assert.notNull(options.publicKey(), "Public key must not be null");
        return doFinal(Cipher.ENCRYPT_MODE, data, options);
    }

    /**
     * 解密
     *
     * @param data    待解密数据
     * @param options 加密器选项
     * @return 解密后的数据
     */
    @Override
    public byte[] decrypt(byte[] data, AsymmetricEncryptorOptions options) {
        checkOptions(options);
        Assert.notNull(options.privateKey(), "Private key must not be null");
        return doFinal(Cipher.DECRYPT_MODE, data, options);
    }

    /**
     * 初始化Cipher实例
     *
     * @param instance   Cipher实例
     * @param cipherMode 加解密模式，加密：{@link Cipher#ENCRYPT_MODE}，解密：{@link Cipher#DECRYPT_MODE}
     * @param options    加密器选项
     * @throws Exception
     */
    @Override
    protected void initCipherInstance(Cipher instance, int cipherMode, AsymmetricEncryptorOptions options) throws Exception {
        Key key = Cipher.ENCRYPT_MODE == cipherMode ? getPublicKey(options.publicKey()) : getPrivateKey(options.privateKey());
        instance.init(cipherMode, key);
    }

    /**
     * 获取公钥
     *
     * @param publicKey 公钥
     * @return PublicKey
     */
    protected PublicKey getPublicKey(byte[] publicKey) throws Exception {
        return getKeyFactory().generatePublic(getPublicKeySpec(publicKey));
    }

    /**
     * 获取私钥
     *
     * @param privateKey 私钥
     * @return PrivateKey
     */
    protected PrivateKey getPrivateKey(byte[] privateKey) throws Exception {
        return getKeyFactory().generatePrivate(getPrivateKeySpec(privateKey));
    }

    /**
     * 获取KeyFactory实例
     *
     * @return
     */
    protected abstract KeyFactory getKeyFactory() throws NoSuchAlgorithmException;

    /**
     * 获取公钥KeySpec
     *
     * @param publicKey 公钥
     * @return 公钥KeySpec
     */
    protected abstract KeySpec getPublicKeySpec(byte[] publicKey);

    /**
     * 获取私钥KeySpec
     *
     * @param privateKey 私钥
     * @return 私钥KeySpec
     */
    protected abstract KeySpec getPrivateKeySpec(byte[] privateKey);

}
